﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;

namespace EveruComponent.Parser.BBCode
{
    public class BBCodeParser
    {


        public BBCodeParser(bool UseDafaultTapPool)
        {
            if (UseDafaultTapPool)
                LoadDefaulTagPool();


        }

        public void LoadDefaulTagPool()
        {
            TagPool.AddDefinition(BBTagDefinitionElement.Create(BBTagDefinition.TagType.ContentTag, "b"));
            TagPool.AddDefinition(BBTagDefinitionElement.Create(BBTagDefinition.TagType.ContentTag, "i"));
            TagPool.AddDefinition(BBTagDefinitionElement.Create(BBTagDefinition.TagType.ContentTag, "u"));
            TagPool.AddDefinition(BBTagDefinitionElement.Create(BBTagDefinition.TagType.ContentTag, "center"));
            TagPool.AddDefinition(BBTagDefinitionElement.Create(BBTagDefinition.TagType.ContentTag, "url"));
            TagPool.AddDefinition(BBTagDefinitionElement.Create(BBTagDefinition.TagType.ContentTag, "img"));
            TagPool.AddDefinition(BBTagDefinitionElement.Create(BBTagDefinition.TagType.ContentTag, "quote"));

            TagPool.AddDefinition(BBTagDefinitionElement.Create(BBTagDefinition.TagType.PropertyTag,"quote"));
            TagPool.AddDefinition(BBTagDefinitionElement.Create(BBTagDefinition.TagType.PropertyTag, "url"));
            TagPool.AddDefinition(BBTagDefinitionElement.Create(BBTagDefinition.TagType.PropertyTag, "font"));
            TagPool.AddDefinition(BBTagDefinitionElement.Create(BBTagDefinition.TagType.PropertyTag, "color"));
            TagPool.AddDefinition(BBTagDefinitionElement.Create(BBTagDefinition.TagType.PropertyTag, "size"));
        }




        private BBTagDefinition _TagPool = new BBTagDefinition();



        /// <summary>
        /// Contains all possible tags for the actual Parser. Tags who are not included to this list will not be parsed.
        /// </summary>
        public BBTagDefinition TagPool
        {
            get { return _TagPool; }
        }





        /// <summary>
        /// Parse a BBCode To a List of BBElements.
        /// </summary>
        /// <param name="BBCode">Thee BBCode to parse</param>
        public BBBlockList ParseToBBList(string BBCode)
        {
            BBBlockList BlockList = new BBBlockList();

            List<BBElement> BBElements = new List<BBElement>();

            char[] BBCodeArray = BBCode.ToCharArray();
            bool TagListener = false;
            string TagListenerStatement = "";

            List<IBBTag> ActiveTags = new List<IBBTag>();

            BBElement ActualElement = new BBElement();

            foreach (char c in BBCode)
            {
                if (!TagListener)
                {
                    if (c == '[')
                    {
                        TagListenerStatement = "[";
                        TagListener = true;
                    }
                    else
                    {
                        ActualElement.Text += c;
                    }
                }


                else
                {
                    #region Interpretate Tag Statement
                    if (c == ']')
                    {
                        try
                        {
                            bool TagEnd;
                            IEnumerable<BBTagDefinitionElement> TagQuery;
                            BBTagDefinitionElement Tag;




                            string TagString = TagListenerStatement.TrimStart('[').TrimEnd(']');
                            TagEnd = false;
                            if (TagString.StartsWith("/"))
                            {
                                TagEnd = true;
                                TagString = TagString.TrimStart('/');
                            }
                            string[] TagSplits = TagString.Split(" =".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);

                            TagQuery = from o in TagPool._TagPool where o.Tag.ToLower() == TagSplits[0].ToLower() select o;

                            if (TagQuery.Count() < 1)
                            {
                                //Output as Test
                                ActualElement.Text += TagListenerStatement + "]";
                                TagListenerStatement = "";
                                TagListener = false;
                                continue;
                            }


                            Tag = TagQuery.First();




                            if (!TagEnd)
                            {
                                #region If Is BBSimpleTag
                                if (Tag.TagType == BBTagDefinition.TagType.SimpleTag)
                                {
                                    BBElements.Add(ActualElement);
                                    ActualElement = new BBElement();
                                    ActualElement.Tags = ActiveTags;

                                    IBBTag TagInstance = CreateStatementInstance(Tag, TagSplits);

                                    BBElements.Add((BBSimpleTag)TagInstance);
                                }
                                #endregion
                                else if(Tag.TagType == BBTagDefinition.TagType.ContentTag || Tag.TagType == BBTagDefinition.TagType.PropertyTag)
                                {
                                    IBBTag TagInstance = CreateStatementInstance(Tag, TagSplits);

                                    if (TagInstance == null)
                                    {
                                        ActualElement.Text += TagListenerStatement + "]";
                                        TagListener = false;
                                        TagListenerStatement = "";
                                        continue;
                                    }


                                    BBElements.Add(ActualElement);

                                    ActiveTags.Add(TagInstance);

                                    ActualElement = new BBElement();
                                    ActualElement.Tags = new List<IBBTag>();
                                    ActualElement.Tags.AddRange(ActiveTags);

                                }
                            }
                            else
                            {
                                BBElements.Add(ActualElement);


                                var InnerTagQuery = from o in ActiveTags where o.Tag == Tag.Tag select o;
                                if (InnerTagQuery.Count() < 1)
                                {
                                    ActualElement.Text += TagListenerStatement + "]";
                                    TagListener = false;
                                    TagListenerStatement = "";
                                    continue;
                                }

                                ActiveTags.Remove(InnerTagQuery.Last());

                                ActualElement = new BBElement();
                                ActualElement.Tags = new List<IBBTag>();
                                ActualElement.Tags.AddRange(ActiveTags);

                            }




                        }
                        catch
                        {
                            //Tag als Text ausgeben
                        }
                        finally
                        {
                            TagListener = false;
                            TagListenerStatement = "";
                        }
                    }
                    else
                    {
                        TagListenerStatement += c;
                    } 
                    #endregion
                }



            }



            if (TagListener)
            {
                ActualElement.Text += TagListenerStatement;
            }

            BBElements.Add(ActualElement);


            BlockList.Elements = BBElements;
            BlockList.TagPool = TagPool;

            return BlockList;
        }

        /// <summary>
        /// Parse a BB Code To a BBFloating List. More Information at the Documentation
        /// </summary>
        /// <param name="BBCode"></param>
        /// <returns></returns>
        public BBFloatingList ParseToFloatingList(string BBCode)
        {
            BBFloatingList FloatingList = new BBFloatingList();




            return FloatingList;
        }


        /// <summary>
        /// Analyze a Tag Statement for the BB Block parser
        /// </summary>
        /// <param name="TagListenerStatement"></param>
        /// <param name="ActualElement"></param>
        /// <param name="TagEnd"></param>
        /// <param name="TagQuery"></param>
        /// <param name="Tag"></param>
        private void AnalyzeTagStatement(string TagListenerStatement, BBElement ActualElement, out bool TagEnd, out IEnumerable<BBTagDefinitionElement> TagQuery, out BBTagDefinitionElement Tag)
        {
            string TagString = TagListenerStatement.TrimStart('[').TrimEnd(']');
            TagEnd = false;
            if (TagString.StartsWith("/"))
            {
                TagEnd = true;
                TagString = TagString.TrimStart('/');
            }
            string[] TagSplits = TagString.Split(' ', '=');

            TagQuery = from o in TagPool._TagPool where o.Tag == TagSplits[0] select o;

            if (TagQuery.Count() != 1)
            {
                //Output as Test
                ActualElement.Text += TagListenerStatement + "]";
            }


            Tag = TagQuery.First();
        }

        /// <summary>
        /// Create an Instance of a Tag Definition
        /// </summary>
        /// <param name="Element"></param>
        /// <param name="StatementSplits"></param>
        /// <returns></returns>
        private IBBTag CreateStatementInstance(BBTagDefinitionElement Element, string[] StatementSplits)
        {
            if (Element.TagType == BBTagDefinition.TagType.SimpleTag)
            {
                return new BBSimpleTag() { Tag = Element.Tag };
            }
            else if (Element.TagType == BBTagDefinition.TagType.ContentTag)
            {
                return new BBContentTag() { Tag = Element.Tag };
            }
            else if (Element.TagType == BBTagDefinition.TagType.PropertyTag)
            {
                if (StatementSplits.Count() < 2)
                {
                    return null;
                }

                return new BBPropertyTag() { Tag = Element.Tag, Property = StatementSplits[1] };
            }

            return null;
        }






    }
}
